﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using Util.Atomic;

namespace BaiduQuiz.Util.Concurrent
{
    public class ConcurrentLinkedQueue<T>
    {
        private class _Entry<K>
        {
            internal K element;
            internal _Entry<K> next;

            public _Entry(K element, _Entry<K> next)
            {
                this.element = element;
                this.next = next;
            }
        }

        private _Entry<T> head;
        private _Entry<T> tail;

        private ManualResetEvent eve;
        private AtomicInteger count;

        public ConcurrentLinkedQueue()
        {
            head = new _Entry<T>(default(T), null);
            tail = head;
            eve = new ManualResetEvent(false);
            count = new AtomicInteger(0);
        }

        public bool IsEmpty
        {
            get
            {
                return (head.next == null);
            }
        }
        public int Count
        {
            get
            {
                return count.Get();
            }
        }

        public void Enqueue(T element)
        {
            _Entry<T> entry = new _Entry<T>(element, null);
            while (true)
            {
                _Entry<T> curTail = tail;
                _Entry<T> residue = curTail.next;
                if (curTail == tail)
                {
                    if (residue == null)
                    {
                        if (Interlocked.CompareExchange<_Entry<T>>(ref curTail.next, entry, residue) == residue)
                        {
                            Interlocked.CompareExchange<_Entry<T>>(ref tail, entry, curTail);
                            count.IncrementAndGet();
                            eve.Set();
                            return;
                        }
                    }
                    else
                    {
                        Interlocked.CompareExchange<_Entry<T>>(ref tail, residue, curTail);
                    }
                }
            }
        }
        public T Dequeue()
        {
            _Entry<T> curHead;
            _Entry<T> curTail;
            _Entry<T> next;
            do
            {
                curHead = head;
                curTail = tail;
                next = curHead.next;
                if (curHead == head)
                {
                    if (next == null)
                    {
                        return default(T);
                    }
                    if (curHead == curTail)
                    {
                        Interlocked.CompareExchange<_Entry<T>>(ref tail, next, curTail);
                    }
                    else
                    {
                        T result = next.element;
                        if (Interlocked.CompareExchange<_Entry<T>>(ref head, next, curHead) == curHead)
                        {
                            count.DecrementAndGet();
                            return result;
                        }
                    }
                }
            } while (true);
        }
        public T Dequeue(int timeout)
        {
            DateTime start = DateTime.Now;
            while (true)
            {
                T element = this.Dequeue();
                if (element != null)
                {
                    return element;
                }
                if (timeout == Timeout.Infinite)
                {
                    eve.WaitOne();
                }
                else
                {
                    int curWait = (int)(timeout - DateTime.Now.Subtract(start).TotalMilliseconds);
                    if (curWait <= 0)
                    {
                        return default(T);
                    }
                    eve.WaitOne(curWait);
                }
            }
        }
        public void Dequeue(int timeout, Action<T> selector)
        {
            new Action(() =>
            {
                T element = this.Dequeue(timeout);
                selector(element);
            }).BeginInvoke(null, null);
        }
    }
}
